/************************************************************************/
/*    Author: Scott Bevin                                               */
/************************************************************************/

#pragma once

#include "../XDelegates/Event.h"
#include "IProperty.h"

namespace XF
{
	template<class T>
	class PropertyData
	{
	public:
		T mValue;
		std::string mName;

		XF::Event2<void, T, T> mOnValueChanged;
	};

	template<typename T>
	class Prop: public IProperty
	{
	public:
		typedef T DataType;

	protected:
		PropertyData<T> mData;
		std::string mTypeId;

	public:
		Prop(std::string typeID, const std::string &name)
		{
			mTypeId = typeID;
			mData.mName = name;
		}

		virtual ~Prop(){}

		XF::Event2<void, T, T> &OnValueChanged(){return mData.mOnValueChanged;}

		void Set(const T& value)
		{
			if(mData.mValue != value)
			{
				T oldValue = mData.mValue;
				mData.mValue = value;

				mData.mOnValueChanged.Invoke(oldValue, mData.mValue);
			}
		}

		const T& Get() const {return mData.mValue;}

		std::string Name()
		{
			return mData.mName;
		}

		std::string TypeID()
		{
			return mTypeId;
		}

		Prop<T> operator= (const Prop<T> &rhs);
		Prop<T> operator= (const T& rhs);

		Prop<T> operator+= (const Prop<T> &rhs);
		Prop<T> operator+= (const T& rhs);

		Prop<T> operator-= (const Prop<T> &rhs);
		Prop<T> operator-= (const T& rhs);

		bool operator== (const Prop<T> &rhs);
		bool operator== (const T& rhs);

		bool operator!= (const Prop<T> &rhs);
		bool operator!= (const T& rhs);

		bool operator> (const Prop<T> &rhs);
		bool operator> (const T& rhs);

		bool operator< (const Prop<T> &rhs);
		bool operator< (const T& rhs);

		operator T() const {return mData.mValue;}
	};

	template<class T>
	inline Prop<T> Prop<T>::operator =(const Prop<T> &rhs)
	{
		mData = rhs.mData;
		return *this;
	}

	template<class T>
	inline Prop<T> Prop<T>::operator =(const T& rhs)
	{
		Set(rhs);
		return *this;
	}


	template<class T>
	inline Prop<T> Prop<T>::operator +=(const Prop<T> &rhs)
	{
		Set(mData.mValue + rhs.mData.mValue);
		return *this;
	}

	template<class T>
	inline Prop<T> Prop<T>::operator+= (const T& rhs)
	{
		Set(mData.mValue + rhs);
		return *this;
	}


	template<class T>
	inline Prop<T> Prop<T>::operator-= (const Prop<T> &rhs)
	{
		Set(mData.mValue - rhs.mData.mValue);
		return *this;
	}

	template<class T>
	inline Prop<T> Prop<T>::operator-= (const T& rhs)
	{
		Set(mData.mValue - rhs); //chris (slight fail Set(mData.mValue + rhs) =]
		return *this;
	}


	template<class T>
	inline bool Prop<T>::operator== (const Prop<T> &rhs)
	{
		return mData == rhs.mData;
	}

	template<class T>
	inline bool Prop<T>::operator== (const T& rhs)
	{
		return (mData.mValue == rhs);
	}


	template<class T>
	inline bool Prop<T>::operator!= (const Prop<T> &rhs)
	{
		return mData != rhs.mData;	
	}

	template<class T>
	inline bool Prop<T>::operator!= (const T& rhs)
	{
		return (mData.mValue != rhs);
	}


	template<class T>
	inline bool Prop<T>::operator> (const Prop<T> &rhs)
	{
		return (mData.mValue > rhs.mData.mValue);
	}

	template<class T>
	inline bool Prop<T>::operator> (const T& rhs)
	{
		return (mData.mValue > rhs);
	}


	template<class T>
	inline bool Prop<T>::operator< (const Prop<T> &rhs)
	{
		return (mData.mValue < rhs.mData.mValue);
	}

	template<class T>
	inline bool Prop<T>::operator< (const T& rhs)
	{
		return (mData.mValue < rhs);
	}
}